#ifdef DO_NOT_DEFINE_ME

#include <zzheng/xml/dom/libxml2/libxml2_node.hpp>
//#include "nanjing/xml/libxml2/dom/libxml2_doc.hpp"
//#include "nanjing/xml/dom/dom_error.hpp"
//#include "nanjing/xml/dom/xmlenum.hpp"

#include <cassert>
#include <string>
#include <libxml/tree.h>



namespace zzheng { namespace xml { namespace dom { namespace libxml2_ {

    ////////////////////////////////////////////////////////////////////////////////////////////////
    // constructor, destructor, clone
    //

    libxml2_node::libxml2_node(xmlNode* pimpl): pimpl_(pimpl) {
        assert(pimpl_ != 0 && "libxml2_node() called with null underlying libxml2 node");
    }

    libxml2_node::~libxml2_node() {
        pimpl_ = 0;
    }

    libxml2_node* libxml2_node::clone(bool recursive) const {
        // The third parameter (extends) of xmlDocCopyNode takes the value 1 or 2, as following:
        // - 1 to do a recursive copy (attributes, namespaces and children when applicable);
        // - 2 to copy attributes and namespaces (when applicable).
        xmlNode* px = xmlDocCopyNode(pimpl_, pimpl_->doc, (recursive ? 1 : 2));
        if (px == 0) {
            throw library_err("fail to clone the underlying libxml2 node: null returned");
        }
        // Add the cloned libxml2 node object to the owner document's orphan list.
        libxml2_node* cloned = static_cast<libxml2_node*>(px->_private);
        const libxml2_doc* owner = owner_document();
        assert(cloned != 0 && "fail to clone the underlying libxml2 node: node was not created");
        assert(owner != 0  && "fail to clone the underlying libxml2 node: owner document is null");
        owner->push_orphan(cloned);
        return cloned;
    }


    ////////////////////////////////////////////////////////////////////////////////////////////////
    // const accessors to node properties
    //

    node_t libxml2_node::type() const {
        return to_node_type(pimpl_->type);
    }

    /*bool libxml2_node::is_root() const {
        if (pimpl_->parent != 0 && pimpl_->parent->type == XML_DOCUMENT_NODE) {
            assert(type_ == xmlenum::ELEM_NODE && "root element is not of element type");
            return true;
        } else {
            return false;
        }
    }*/

    std::string libxml2_node::name() const {
        assert(has_name(pimpl_->type) && "name() called on nameless node");
        return to_chars(pimpl_->name);
    }

    std::string libxml2_node::uri() const {
        assert(has_name(pimpl_->type) && "uri() called on nameless node");
        if (pimpl_->ns != 0) {
            return to_chars(pimpl_->ns->href);
        } else {
            return std::string();
        }
    }

    std::string libxml2_node::prefix() const {
        assert(has_name(pimpl_->type) && "prefix() called on nameless node");
        if (pimpl_->ns != 0) {
            return to_chars(pimpl_->ns>prefix);
        } else {
            return std::string();
        }
    }

    std::string libxml2_node::value() const {
        assert(has_value(pimpl_->type) && "value() called on valueless node");
        std::string value;
        switch (pimpl_->type) {
        case XML_ATTRIBUTE_NODE:
            // For attribute node, the value is saved as a child text node.
            if (pimpl_->children != 0) {
                value = to_chars(pimpl_->children->content);
            }
            break;
        case XML_TEXT_NODE:
        case XML_CDATA_SECTION_NODE:
        case XML_COMMENT_NODE:
            // For text, CDATA and comment node, the value is saved as its content.
            value = to_chars(pimpl_->content);
            break;
        default:
            // For other node types: not implemented yet.
            value = "not-implemented";
            break;
        }
        return value;
    }


    ////////////////////////////////////////////////////////////////////////////////////////////////
    // modifiers to node's properties
    //

    void libxml2_node::set_name(const std::string& name) {
        assert(has_name(pimpl_->type) && "set_name() called on nameless node");
        xmlNodeSetName(pimpl_, to_xml_chars(name.c_str()));
    }

    /*
    TODO:
    void libxml2_node::set_prefix(std::string const& prefix) {
        assert(is_namefull(type_) && "set_prefix() called on nameless node");
        prefix_.clear();
        xmlChar const* prefix_xstr = (prefix.empty() ? 0 : to_xchars(prefix.c_str()));
        xmlNs* ns = xmlSearchNs(pimpl_->doc, pimpl_, prefix_xstr);
        if (ns != 0) {
            xmlSetNs(pimpl_, ns);
        } else {
            throw namespace_err("fail to set an undeclared namespace prefix to the node");
        }
    }
    */

    void libxml2_node::set_value(const std::string& value) {
        assert(has_value(pimpl_->type) && "set_value() called on valueless node");
        switch (pimpl_->type) {
        case XML_ATTRIBUTE_NODE:
            // For attribute node, the value is saved as a child text node.
            // If the child text node already exists, remove it first.
            if (pimpl_->children != 0) {
                xmlNode* old_px = pimpl_->children;
                xmlUnlinkNode(old_px);
                xmlFreeNode(old_px);
            }
            // Create a new text node and add it to the attribute node as its child.
            if (!value.empty()) {
                xmlNode* new_px = xmlNewText(to_xml_chars(value.c_str()));
                xmlAddChild(pimpl_, new_px);
            }
            break;
        case XML_TEXT_NODE:
        case XML_CDATA_SECTION_NODE:
        case XML_COMMENT_NODE:
            // For text, CDATA and comment node, the value is saved as its content.
            xmlNodeSetContent(pimpl_, to_xml_chars(value.c_str()));
            break;
        default:
            assert(! "implement me");
            break;
        }
    }

    //! \todo should be more careful about namespaces (existing namespace? invalid char?).
    void libxml2_node::add_nsdecl(std::string const& nsuri, std::string const& prefix) {
        assert(is_nsdeclfull(type_) && "add_nsdecl() called on nsdecl-less node");
        // Create a new namespace declaration for this node.
        xmlChar const* nsuri_xstr = (nsuri.empty() ? 0 : to_xchars(nsuri.c_str()));
        xmlChar const* prefix_xstr = (prefix.empty() ? 0 : to_xchars(prefix.c_str()));
        xmlNs* ns = xmlNewNs(pimpl_, nsuri_xstr, prefix_xstr);
        // Check the return value to see whether the namespace is declared successfully.
        if (ns == 0) {
            std::string errmsg;
            errmsg.append("fail to add namespace declaration: ")
                .append("unable to associate prefix ").append(prefix)
                .append(" to uri ").append(nsuri);
            throw library_err(errmsg);
        }
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////
    // accessors to parent, children and attributes
    //

    libxml2_node* libxml2_node::parent() {
        if (pimpl_->parent != 0 && pimpl_->parent->type == XML_ELEMENT_NODE) {
            return static_cast<libxml2_node*>(pimpl_->parent->_private);
        } else {
            return 0;
        }
    }

    libxml2_node* libxml2_node::first_child() {
        assert(pimpl_->type == XML_ELEMENT_NODE && "first_child() called on non-element node");
        if (pimpl_->children != 0) {
            return static_cast<libxml2_node*>(pimpl_->children->_private);
        } else {
            return 0;
        }
    }

    libxml2_node* libxml2_node::last_child() {
        assert(pimpl_->type == XML_ELEMENT_NODE && "last_child() called on non-element node");
        if (pimpl_->last != 0) {
            return static_cast<libxml2_node*>(pimpl_->last->_private);
        } else {
            return 0;
        }
    }

    libxml2_node* libxml2_node::prev_sibling() {
        if (pimpl_->prev != 0) {
            return static_cast<libxml2_node*>(pimpl_->prev->_private);
        } else {
            return 0;
        }
    }

    libxml2_node* libxml2_node::next_sibling() {
        if (pimpl_->next != 0) {
            return static_cast<libxml2_node*>(pimpl_->next->_private);
        } else {
            return 0;
        }
    }

    libxml2_node* libxml2_node::first_attr() {
        assert(pimpl_->type == XML_ELEMENT_NODE && "first_attr() called on non-element node");
        if (pimpl_->properties != 0) {
            return static_cast<libxml2_node*>(pimpl_->properties->_private);
        } else {
            return 0;
        }
    }

    libxml2_node* libxml2_node::last_attr() {
        assert(pimpl_->type == XML_ELEMENT_NODE && "last_attr() called on non-element node");
        xmlAttr* px = 0;
        for (px = pimpl_->properties; px != 0 && px->next != 0; px = px->next) {
            // Do nothing, just iterate to the last attribute.
        }
        if (px != 0) {
            return static_cast<libxml2_node*>(px->_private);
        } else {
            return 0;
        }
    }


    ////////////////////////////////////////////////////////////////////////////////////////////////
    // modifiers to children and attributes
    //

    libxml2_node* libxml2_node::adopt_child(libxml2_node* node) {

        // Assert the node can be adopted as a child element of this node.
        assert(is_childfull(type_) && "adopt_child() called on child-less node");
        assert(node != 0 && is_childable(node->type_)
            && "adopt_child() called with null pointer or non-child-able node");

        // Erase the node to adopt from the orphan list of its owner document.
        libxml2_doc const* owner = node->owner_document();
        assert(owner != 0 && "node to be adopted does not have an owner document");
        owner->erase_orphan(node);

        // Adopt the node as a child element: adjacent text nodes may be merged.
        _xmlNode* px = xmlAddChild(pimpl(), node->pimpl());
        assert(px != 0 && "adopt_child() failed: libxml2 returns null");
        return static_cast<libxml2_node*>(px->_private);
    }

    libxml2_node* libxml2_node::adopt_attr(libxml2_node* node) {

        // Assert the node can be adopted as an attribute of this node.
        assert(is_attrfull(type_) && "adopt_attr() called on attr-less node");
        assert(node != 0 && is_attrable(node->type())
            && "adopt_attr() called with null pointer or non-attr-able node");

        // Erase the node to adopt from the orphan list of its owner document.
        libxml2_doc const* owner = node->owner_document();
        assert(owner != 0 && "node to be adopted does not have an owner document");
        owner->erase_orphan(node);

        // Adopt the node as an attribute: existing attribute may be destroyed.
        // Note: there is a bug in libxml2's xmlAddChild() implementation: if this element has only one
        //       attribute which has the same name as the orphan attribute to adopt, xmlAddChild() will
        //       cause an access violation runtime error (@0x18).
        _xmlNode* px = xmlAddChild(pimpl(), node->pimpl());
        assert(px != 0 && "adopt_attr() failed: libxml2 returns null");
        return static_cast<libxml2_node*>(px->_private);
    }

    libxml2_node* libxml2_node::adopt_child(libxml2_node* location, libxml2_node* node) {
        
        // Assert the node can be adopted as a child element of this node.
        assert(is_childfull(type()) && "adopt_child() called on child-less node");
        assert(node != 0 && is_childable(node->type())
            && "adopt_child() called with null or non-child-able node");
        assert(location != 0 && is_childable(location->type())
            && "adopt_child() called with null or non-child-able location");
        assert(location->parent() == this
            && "adopt_child() called with inconsistent location");

        // Erase the node to adopt from the orphan list of its owner document.
        libxml2_doc const* owner = node->owner_document();
        assert(owner != 0 && "node to be adopted does not have an owner document");
        owner->erase_orphan(node);

        // Adopt the node as a child element: adjacent text nodes may be merged.
        _xmlNode* px = xmlAddPrevSibling(location->pimpl(), node->pimpl());
        assert(px != 0 && "adopt_child() failed: libxml2 returns null");
        return static_cast<libxml2_node*>(px->_private);
    }

    libxml2_node* libxml2_node::adopt_attr(libxml2_node* location, libxml2_node* node) {

        // Assert the node can be adopted as an attribute of this node.
        assert(is_attrfull(type()) && "adopt_attr() called on attr-less node");
        assert(node != 0 && is_attrable(node->type())
            && "adopt_attr() called with null or non-attr-able node");
        assert(location != 0 && is_attrable(location->type())
            && "adopt_attr() called with null or non-attr-able location");
        assert(location->parent() == this
            && "adopt_attr() called with inconsistent location");

        // Erase the node to adopt from the orphan list of its owner document.
        libxml2_doc const* owner = node->owner_document();
        assert(owner != 0 && "node to be adopted does not have an owner document");
        owner->erase_orphan(node);

        // Adopt the node as an attribute: existing attribute may be destroyed.
        _xmlNode* px = xmlAddPrevSibling(location->pimpl(), node->pimpl());
        assert(px != 0 && "adopt_attr() failed: libxml2 returns null");
        return static_cast<libxml2_node*>(px->_private);
    }

    //! Erases the child node from this node. This function is called only when this node can have
    //! children, and the node to erase is a child of this node.
    //! \param node  pointer to the child node to erase.
    void libxml2_node::erase_child(libxml2_node* node) {

        // Assert the node can be erased from the child elements of this node.
        assert(is_childfull(type_) && "erase_child() called on child-less node");
        assert(node != 0 && is_childable(node->type_)
            && "erase_child() called with null pointer or non-child-able node");
        assert(node->parent() == this
            && "erase_child() called with node that is not a child element of this node");

        // Unlink the libxml2 node from its current context.
        // Note: xmlUnlinkNode() does not change the owner document, and does not free the node.
        xmlUnlinkNode(node->pimpl());

        // Push the erased libxml2 node to the orphan list of its owner document.
        libxml2_doc const* owner = node->owner_document();
        assert(owner != 0 && "erased element node does not have an owner document");
        owner->push_orphan(node);
    }

    //! Erases the attribute from this node. This function is called only when this node can have
    //! attributes, and the node to erase is an attribute of this node.
    //! \param node  pointer to the attribute node to erase.
    void libxml2_node::erase_attr(libxml2_node* node) {

        // Assert the node can be erased from the attributes of this node.
        assert(is_attrfull(type_) && "erase_attr() called on attr-less node");
        assert(node != 0 && is_attrable(node->type())
            && "erase_attr() called with null pointer or non-attr-able node");
        assert(node->parent() == this
            && "erase_attr() called with node that is not an attribute of this node");

        // Unlink the libxml2 node from its current context.
        // Note: xmlUnlinkNode() does not change the owner document, and does not free the node.
        xmlUnlinkNode(node->pimpl());

        // Push the erased libxml2 node to the orphan list of its owner document.
        libxml2_doc const* owner = node->owner_document();
        assert(owner != 0 && "erased attribute does not have an owner document");
        owner->push_orphan(node);
    }


    ////////////////////////////////////////////////////////////////////////////////////////////////
    // accessors to owner document and underlying libxml2 node object
    //

    libxml2_doc* libxml2_node::owner_document() {
        assert(pimpl_->doc != 0 && pimpl_->doc->_private != 0
            && "owner_document() returns null");
        return static_cast<libxml2_doc*>(pimpl_->doc->_private);
    }

    libxml2_doc const* libxml2_node::owner_document() const {
        assert(pimpl_->doc != 0 && pimpl_->doc->_private != 0
            && "owner_document() returns null");
        return static_cast<libxml2_doc const*>(pimpl_->doc->_private);
    }

    _xmlNode* libxml2_node::pimpl() {
        return pimpl_;
    }

    _xmlNode const* libxml2_node::pimpl() const {
        return static_cast<_xmlNode const*>(pimpl_);
    }


} } } } // namespace zzheng::xml::dom::libxml2_


#endif // DO_NOT_DEFINE_ME


